using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using BriansClimb;
using BriansClimb.GameObjects;
using BriansClimb.Climber;
using BriansClimb.DrawableGameObjects.Buttons;
using BriansClimb.DrawableGameObjects.ClimbingHolds;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;

namespace BriansClimb.DrawableGameObjects.GameScenes
{
    /// <summary>
    /// This is the scene where the player actually climbs the wall.
    /// </summary>
    public class PlayerClimbScene : GameScene
    {
        //!The background texture.
        protected Texture2D background;
        //!The background bottom texture.
        protected Texture2D backgroundBottom;
        //!The spritebatch to draw the scene on.
        protected SpriteBatch spriteBatch = null;
        
        //!The escape menu.
        protected TextMenuComponent escMenu;
        //!The text menuc omponent.
        protected TextMenuComponent textMenuComponent;

        //!The list of climbing holds on the loaded wall.
        protected List<ClimbingHold> activeWallList;
        //!The list of highest climbing holds, used to know when the player has won.
        protected List<ClimbingHold> highestHoldList;
        
        //!The master game object.
        protected Game game;
        //!Another master game object.
        protected Game briansClimb;
        
        //!If the mouse is clicked.
        protected bool clicked;
        //!If the mouse is pressed.
        protected bool pressed;
        //!Indicates the first update run.
        protected bool first = true;
        //!Indicates if the highest hold has been reached.
        protected bool highestHoldReached = false;
        //!Indicates if the climber is falling.
        protected bool climberIsFalling = false;
        //!Indicates if the scene is fading to black.
        protected bool fadedToBlack = false;

        //!The alpha value of the scene.
        protected int alphaValue = 0;

        //!The BodyPart of the floor.  Used during collisions.
        protected BodyPart floor;

        //!The previous gamepad state.
        protected GamePadState oldGamePadButtonState;
        //!The current mouse state.
        protected MouseState mouseCurrentState;

        //!The previous keyboard state.
        protected KeyboardState oldKeyboardState;
        //!The current keyboard state.
        protected KeyboardState keyboardState;

        //!The normal color, used for tinting.
        protected Color normalColor = Color.White;
        //!The paused color, used for tinting.
        protected Color pausedColor = Color.Gray;
        //!Color variable used for switching.
        protected Color theColor;

        //!The stamina bar used.
        protected StaminaBar staminaBar;

        //!The physics simulator.
        protected PhysicsSimulator physicsSimulator;
        //!The climber.
        protected BriansClimb.Climber.Climber climber;
        //!The body part info handler used to send wall lists.
        protected BriansClimb.GameObjects.BodyPartInfoHandler bpih;
        //!The filestat manager used to send stats & filenames.
        protected FileStatManager fileStatManager;
        //!The audio manager for sounds.
        protected AudioManager audioManager;

        //!Indicates if the gravity is on.
        protected bool firstGravOn = false;

        /// <summary>
        /// The constructor for the player climb scene.
        /// </summary>
        /// <param name="game">Master game object.</param>
        /// <param name="background">Background texture</param>
        /// <param name="textMenuComponent">Text menu component</param>
        /// <param name="fileStatManagerIn">Master filestat manager</param>
        /// <param name="physicsSimulator">Master physics simulator</param>
        public PlayerClimbScene(Game game, Texture2D background, TextMenuComponent textMenuComponent, FileStatManager fileStatManagerIn,
            PhysicsSimulator physicsSimulator)
            : base(game)
        {
            //gets the current sprite batch
            spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            audioManager = (AudioManager)Game.Services.GetService(typeof(AudioManager));

            //gets the current keyboard state
            oldKeyboardState = Keyboard.GetState();
            oldGamePadButtonState = GamePad.GetState(PlayerIndex.One);
            
            this.game = game;
            this.background = background;
            this.physicsSimulator = physicsSimulator;
            //this.physicsSimulator = (PhysicsSimulator)Game.Services.GetService(typeof(PhysicsSimulator));

            backgroundBottom = game.Content.Load<Texture2D>(".\\Textures\\SceneToolBar");         
            
            bpih = new BodyPartInfoHandler(game);
            escMenu = textMenuComponent;
            escMenu.Hide();
            escMenu.Hide();
            
            this.fileStatManager = fileStatManagerIn;
            activeWallList = new List<ClimbingHold>();
            highestHoldList = new List<ClimbingHold>();
            Components.Add(fileStatManager);

            staminaBar = new StaminaBar(game, 5, new Vector2(430, 958), this);
            staminaBar.Enabled = false;
            Components.Add(staminaBar);

            //creating the climber
            climber = new BriansClimb.Climber.Climber(game, bpih);
            float climberFriction = fileStatManager.climberFriction;
            float climberFlexibility = fileStatManager.ClimberFlexibility;
            climber.setStats(0, 250, 200, 200, climberFriction, physicsSimulator, game, new Vector2(500, 728));
            Components.Add(climber);
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            
            base.Initialize();
        }
 
        /// <summary>
        /// Property that represents the 'esc' menu object
        /// </summary>
        public TextMenuComponent EscMenu
        {
            get { return escMenu; }
        }

        /// <summary>
        /// Property that indicates if the highest hold has been reached or not
        /// </summary>
        public bool HighestHoldReached
        {
            get { return highestHoldReached; }
            set { highestHoldReached = value; }
        }

        /// <summary>
        /// Property that indicates if the climber is falling.
        /// </summary>
        public bool ClimberIsFalling
        {
            get { return climberIsFalling; }
            set { climberIsFalling = value; }            
        }

        /// <summary>
        /// Property that represents if the scene has faded to black.
        /// </summary>
        public bool FadedToBlack
        {
            get { return fadedToBlack; }
        }

        /// <summary>
        /// Property that represents the climber.
        /// </summary>
        public BriansClimb.Climber.Climber Climber
        {
            get { return climber; }
        }

        /// <summary>
        /// Property that represents the stamina bar
        /// </summary>
        public StaminaBar StaminaBar
        {
            get { return staminaBar; }
        }
        
        /// <summary>
        /// Property that represents the selected menu option for the 'esc' menu
        /// </summary>
        public int EscSelectedMenuIndex
        {
            get { return escMenu.SelectedItemIndex; }
            set { escMenu.SelectedItemIndex = value; }
        }

        /// <summary>
        /// Property that represents the climbing hold list.
        /// </summary>
        public List<ClimbingHold> ClimbingHoldList
        {
            get { return activeWallList; }
            set { activeWallList = value; }
        }

        /// <summary>
        /// Show the Wall Customize Scene
        /// </summary>
        public override void Show()
        {
            //sets the initial state of the 'esc' menu to hidden
            escMenu.Hide();

            //color of background is normal white tint
            theColor = normalColor;

            base.Show();
        }

        /// <summary>
        /// Hide the Wall Customize Scene
        /// </summary>
        public override void Hide()
        {
            //makes sure the 'esc' menu is not showing
            escMenu.Hide();

            base.Hide();
        }

        /// <summary>
        /// Loads the climbing holds into the component list from the 
        /// active wall list.
        /// </summary>
        public void LoadWall()
        {
            foreach (ClimbingHold ch in activeWallList)
            {
                //check to see if the hold object already exists in the components list
                if (!Components.Contains(ch))
                    Components.Add(ch);
                if (!ch.Visible)
                {
                    Components.Remove(ch);
                }
            }

        }

        /// <summary>
        /// Shows the 'esc' menu.
        /// </summary>
        public void ShowEscMenu()
        {
            //creating the 'esc' menu
            string[] menuItems = { "Resume", "Main Menu", "Load" };
            escMenu.SetMenuItems(menuItems);

            // Put the menu centered in screen
            escMenu.Position = new Vector2((Game.Window.ClientBounds.Width -
                                          escMenu.Width) / 2, 300);

            //set the 'esc' menu to be active
            escMenu.Show();

            //when scene is paused, background has grey tint
            theColor = pausedColor;

            base.Pause();
        }
        
        /// <summary>
        /// Checks for the 'esc' key click(press & release).
        /// </summary>
        /// <returns>True for a click, False otherwise</returns>
        private bool CheckEscMenuClick()
        {
            keyboardState = Keyboard.GetState();

            bool escResult = (oldKeyboardState.IsKeyDown(Keys.Escape) &&
                keyboardState.IsKeyUp(Keys.Escape));
            oldKeyboardState = keyboardState;
            return escResult;
        }

        /// <summary>
        /// Checks the stamina bar.  If it is empty, indicates to the climber to let go.
        /// </summary>
        private void checkStamina()
        {
            if (staminaBar.NoStamina)
            {
                climber.letGo();
                climberIsFalling = climber.HasLetGo;
            }
        }

        /// <summary>
        /// Checks to see if the highest hold is being held, sets property to true if it is.
        /// </summary>
        private void checkHighest()
        {
            for(int i=0;i<highestHoldList.Count;i++)
            {
                if ((highestHoldList[i].IsBeingHeld)&&(highestHoldList[i].HeldBy!="leftFoot")&&(highestHoldList[i].HeldBy!="rightFoot"))
                {            
                    highestHoldList.Remove(highestHoldList[i]);
                    highestHoldReached = true;   
                }
            }            
        }

        /// <summary>
        /// Resets the climber.  Lots of issues with this.
        /// </summary>
        public void resetClimber()
        {
            
            Components.Remove(climber);
            climber.Enabled = false;
            climber.Visible = false;
            climber.Dispose();
            /*
            physicsSimulator.Clear();
            physicsSimulator.geomList.Clear();
            physicsSimulator.bodyList.Clear();
            physicsSimulator.controllerList.Clear();
            physicsSimulator.jointList.Clear();
            physicsSimulator.arbiterList.Clear();
            physicsSimulator.Clear();
            */
            Console.Write("Geom list length after: ");            
            
            climber = new BriansClimb.Climber.Climber(game, bpih);
            climber.reset(0, 250, 200, 200, fileStatManager.climberFriction, physicsSimulator, game, new Vector2(500,700));
            physicsSimulator.Gravity = new FarseerGames.FarseerPhysics.Mathematics.Vector2(0, 0);
            Console.WriteLine("CLIMBER HAS BEEN RESET IN PCS");
            Components.Add(climber);
        }

        /// <summary>
        /// Hides the climber, the holds, and the stamina bar.
        /// </summary>
        private void HideClimberHoldsAndStaminaBar()
        {
            foreach (ClimbingHold ch in activeWallList)
            {
                ch.Visible = false;                
            }

            staminaBar.Visible = false;

            if (alphaValue > 240)
                climber.Visible = false;
        }
        
        /// <summary>
        /// Determine the highest hold and adds it to the highest hold list.
        /// </summary>
        private void getHighestHold()
        {
            ClimbingHold highestHold = activeWallList[0];
            foreach (ClimbingHold ch in activeWallList)
            {
                if (ch.ClimbingHoldPosition.Y < highestHold.ClimbingHoldPosition.Y)
                    highestHold = ch;
            }
            highestHoldList.Add(highestHold);
            int j = highestHoldList.Count;
            for(int i=0;i<j;i++)
            {   
                foreach (ClimbingHold ch in activeWallList)
                {
                    if ((highestHoldList[i].ClimbingHoldPosition.Y == ch.ClimbingHoldPosition.Y)&&(highestHoldList[i]!=ch))
                        highestHoldList.Add(ch);
                }
            }
            first = false;
        }

        /// <summary>
        /// Checks to see if the climber is currently falling.
        /// </summary>
        void checkFalling()
        {
            if (climber.HasLetGo)
                climberIsFalling = true;
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            //Determining the highest hold in the wall.
            if (first)
                getHighestHold();             
            physicsSimulator.Update(gameTime.ElapsedGameTime.Milliseconds * .0006f);
            //check for the 'esc' key press
            if (CheckEscMenuClick())
                ShowEscMenu();
            LoadWall();
            //Giving the climber the current wall list
            climber.setWallList(activeWallList);

            //Needed to keep climber on top
            Components.Remove(climber);
            Components.Add(climber);

            //Check the stamina bar, the condition of the highest hold, and if the climber is falling
            checkStamina();
            checkHighest();
            checkFalling();

            //Increments the stamina bar if gravity has been turned on
            if (physicsSimulator.Gravity != new FarseerGames.FarseerPhysics.Mathematics.Vector2(0, 0))
            {
                staminaBar.ConstantDrop = fileStatManager.StaminaBarValue;
                staminaBar.Enabled = true;
                if (!firstGravOn)
                {
                    firstGravOn = !firstGravOn;
                }
            }
            
           
            //Dims the screen if the climber is falling
            if (climberIsFalling || highestHoldReached)
                if (alphaValue < 256)
                    alphaValue = alphaValue + 2;

            //Hides the holds & stamina bar
            if (alphaValue > 50)
                HideClimberHoldsAndStaminaBar();

            //Fades to black.
            if (alphaValue > 252)
                fadedToBlack = true;

            base.Update(gameTime);
        }

        /// <summary>
        /// Allows the game component to draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            //draw the background filling the whole screen
            spriteBatch.Draw(background, new Rectangle(0, 0, Game.Window.ClientBounds.Width,
                                Game.Window.ClientBounds.Height), theColor);
            //draws the 'floor'
            spriteBatch.Draw(backgroundBottom, new Rectangle(6, 950, 1268, 68), Color.White);


            if (climberIsFalling || highestHoldReached)                
                spriteBatch.Draw(background, new Rectangle(0, 0, Game.Window.ClientBounds.Width,
                                game.Window.ClientBounds.Height), new Color(0, 0, 0, (byte)alphaValue));
           
            base.Draw(gameTime);
        }
    }
}
